package com.fwtai;

import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.Log4JLoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.file.FileSystem;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerFileUpload;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.HTreeMap;
import org.mapdb.Serializer;

import java.io.File;

/**
 * 文件上传
 * @作者 田应平
 * @版本 v1.0
 * @创建时间 2021-06-30 11:05
 * @QQ号码 444141300
 * @Email service@dwlai.com
 * @官网 http://www.fwtai.com
*/
public class Launcher extends AbstractVerticle{

  private static final String rootPath = "C:/upload/";

  private static final String url = "http://127.0.0.1:7778/file?path=";

  private final InternalLogger log = Log4JLoggerFactory.getInstance(getClass());

  private DB db;

  private HTreeMap<String,String> map;

  private FileSystem fileSystem;

  @Override
  public void start(final Promise<Void> startPromise) throws Exception{
    fileSystem = vertx.fileSystem();
    final Future<Boolean> initFuture = Future.succeededFuture();
    initDb(initFuture);
    initFuture.onComplete(res->{
      if(res.succeeded()){
        final Future<Void> uploadFuture = Future.succeededFuture();
        final Future<Void> downloadFuture = Future.succeededFuture();
        upload(uploadFuture);
        download(downloadFuture);
        CompositeFuture.all(uploadFuture,downloadFuture).onComplete(server->{
          if(server.succeeded()){
            startPromise.complete();
            log.info("server 全部部署成功");
          }else{
            server.cause().printStackTrace();
            log.error(server.cause().getMessage());
            startPromise.fail(server.cause());
          }
        });
      }else{
        log.error(res.cause().getMessage());
        startPromise.fail(res.cause());
      }
    });
  }

  private void saveFile(final HttpServerFileUpload upload,final String savePath,final String name,final String uuid,final String nameDb,final String time,final String urlPath){
    upload.streamToFileSystem(savePath).onComplete(end->{
      final long size = upload.size();
      System.out.println("size : " + size + " bytes");
      final String type = Utils.type(name);
      final JsonObject json = new JsonObject().put("id",uuid).put("name",nameDb).put("realName",upload.filename()).put("path",savePath).put("type",type).put("createTime",time).put("url",urlPath).put("size",size);
      System.out.println(Json.encodePrettily(json));
      map.put(uuid,json.toString());
    });
  }

  private void initDb(final Future<Boolean> future){
    try{
      final File file = new File("C:/upload/xn-file-windows.db");
      db = DBMaker.fileDB(file).closeOnJvmShutdown().make();
      map = db.hashMap("xn-file-server",Serializer.STRING,Serializer.STRING).expireStoreSize(1 * 1024 * 1024 * 1024).createOrOpen();
      future.isComplete();
    }catch(Exception e){
      e.printStackTrace();
      future.failed();
    }
  }

  private void upload(final Future<Void> uploadFuture){
    vertx.createHttpServer().requestHandler(req -> {
      req.setExpectMultipart(true);
      req.exceptionHandler(th -> {
        th.printStackTrace();
        log.error(th.getLocalizedMessage());
        respError(req,500,th.getLocalizedMessage());
      });
      final HttpMethod method = req.method();
      final String reqPath = req.path();
      System.out.println("method: " + method + " , reqPath: " + reqPath);
      /*http://192.168.3.108:7777/api/fileUpload?name=d&path=C:\\upload*/
      if(method.equals(HttpMethod.POST) && "/api/fileUpload".equals(reqPath)){
        String name = req.getParam("name"); // 默认 yyyy/MM/dd 文件夹位置 name=xxxx.jpg
        String path = req.getParam("path"); // 带 path 路径 path=wang/xue/hello.jpg
        String fileSavePath = rootPath + Utils.dir();
        System.out.println("name: " + name + " path >> " + fileSavePath);
        String time = Utils.time(); // 当前时间戳
        String uuid = Utils.uuid(); //  UUID 做 key
        if(!StringUtil.isNullOrEmpty(name)){ // 处理 name 参数 上传文件
          HttpServerRequest request = req.uploadHandler(upload -> {
            if(!Utils.checkPath(fileSavePath))
              fileSystem.mkdirsBlocking(fileSavePath);
            System.out.println("fileName : " + upload.filename());
            final String nameDb = time + "-" + name;
            final String savePath = fileSavePath + nameDb;
            final String urlPath = url + uuid;
            System.out.println("path ==> " + savePath);
            saveFile(upload,savePath,name,uuid,nameDb,time,urlPath);
            //resp(req,new JsonObject().put("url",urlPath));
          });
          request.response().end("上传成功");
        }else if(!StringUtil.isNullOrEmpty(path)){ // 处理 path 路径 文件上传
          String[] pathArray = Utils.path(path);
          // 需要检查 该路径是否存在，不存在创建(并检查是否存在 ..) todo 测试 6级文件夹深度
          String pathItem = pathArray[0];     // 参数传入的 路径
          String reqFileName = pathArray[1]; // 参数传入的 fileName
          if(pathItem.indexOf(".") > 0){
            log.error("非法路径 >> " + path);
            respError(req,404,"");
          }else{
            final String savePath = rootPath + pathItem;
            if(!Utils.checkPath(savePath))
              fileSystem.mkdirsBlocking(savePath);
            req.uploadHandler(upload -> {
              System.out.println("fileName : " + upload.filename());
              String nameDb = time + "-" + reqFileName;
              String urlPath = url + uuid;
              String savePathAndFileName = savePath + nameDb;
              System.out.println("path ==> " + savePathAndFileName);
              saveFile(upload,savePathAndFileName,nameDb,uuid,nameDb,time,urlPath);
              resp(req,new JsonObject().put("url",urlPath));
            });
          }
        }else{
          respError(req,404,"");
        }
      }else{
        respError(req,404,"");
      }
    }).listen(7777,res -> {
      if(res.succeeded()){
        log.debug("Upload Server 7777");
        uploadFuture.isComplete();
      }else{
        res.cause().printStackTrace();
        log.error(res.cause().getMessage());
        uploadFuture.failed();
      }
    });
  }

  private void download(final Future<Void> downloadFuture){
    vertx.createHttpServer().requestHandler(req -> {
      req.exceptionHandler(th -> {
        th.printStackTrace();
        log.error(th.getLocalizedMessage());
        respError(req,500,th.getLocalizedMessage());
      });
      HttpMethod method = req.method();
      String urlPath = req.path();
      System.out.println("method: " + method + " , url: " + urlPath);
      if(method.equals(HttpMethod.GET) && "/file".equals(urlPath)){
        String path = req.getParam("path"); // 带 path 路径 path=wang/xue/hello.jpg
        String json = map.get(path);
        if(StringUtil.isNullOrEmpty(json)){
          respError(req,404,"数据不存在");
        }else{
          final JsonObject jsonObject = new JsonObject(json);
          System.out.println(jsonObject.encodePrettily());
          String path1 = jsonObject.getString("path");
          System.out.println("path <<< " + path1);
          req.response().sendFile(path1);
        }
      }else{
        respError(req,404,"路径不对");
      }
    }).listen(7778,res -> {
      if(res.succeeded()){
        log.debug("Download Server 7778");
        downloadFuture.isComplete();
      }else{
        res.cause().printStackTrace();
        log.error(res.cause().getMessage());
        downloadFuture.failed();
      }
    });
  }

  private static void resp(final HttpServerRequest request,final JsonObject ret){
    request.response().putHeader("content-type","application/json;charset=utf-8").putHeader("Access-Control-Allow-Origin","*").putHeader("Access-Control-Allow-Credentials","true").putHeader("Content-Disposition","attachment").end(Json.encodePrettily(ret));
  }

  private static void respError(final HttpServerRequest request,final int code,final String error){
    request.response().putHeader("content-type","application/json;charset=utf-8").putHeader("Access-Control-Allow-Origin","*").putHeader("Access-Control-Allow-Credentials","true").putHeader("Content-Disposition","attachment").setStatusCode(code).end(Json.encodePrettily(new JsonObject().put("error",error)));
  }

  public static String getSuffix(final String originalName){
    return originalName.substring(originalName.lastIndexOf(".")+1);
  }
}